﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using Microsoft.Win32;
using System.IO;

namespace Movistar.SMS.Service
{
    public partial class MovistarService : ServiceBase
    {
        private bool bRunning = false;
        private bool bStopping = false;
        private RemindersDB remindersDB = null;
        private string sLogFilePath = null;
        private string sServiceURL = null;
        private string sServicePass = null;
        private string sServiceMobile = null;

        #region Start/Stoping routines

        public MovistarService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            Thread myThread = new Thread(new ThreadStart(ThreadMain));

            myThread.Start();
        }

        protected override void OnStop()
        {
            bStopping = true;

            while (bRunning)  // Wait for thread to finish
                Thread.Sleep(100);
        }

        #endregion

        #region ThreadMain

        private void ThreadMain()
        {
            try
            {
                RegistryKey key = Registry.LocalMachine.OpenSubKey(Service.Properties.Settings.Default.RegKeyAddIn);

                remindersDB = new RemindersDB(key.GetValue(Service.Properties.Settings.Default.RegKeyDB).ToString());
                sLogFilePath = key.GetValue(Service.Properties.Settings.Default.RegKeyLogFile).ToString();
            }
            catch (Exception e)
            {
                WriteToEventLog("Fallo al inicializar el servicio: " + e.Message, EventLogEntryType.Error);

                Stop(); // This is called in the actual thread. No deadlocks since bRunning is true at this time
            }

            bRunning = true;

            while (!bStopping)
            {
                try
                {
                    // Get appointments with reminders that occurs on the future

                    var nextReminders = from app in remindersDB.Appointments
                                        where app.Occurs >= DateTime.Now
                                        join appRem in remindersDB.AppointmentReminders
                                        on app.AppointmentID_ equals appRem.AppointmentID_
                                        join Rem in remindersDB.Reminders
                                        on appRem.ReminderID_ equals Rem.ReminderID_
                                        select new
                                        {
                                            To = app.To,
                                            TemplateID = app.TemplateID_,
                                            CustomMessage = app.CustomMessage,
                                            Occurs = app.Occurs,
                                            Days = Rem.Days,
                                            Hours = Rem.Hours,
                                            Minutes = Rem.Minutes,
                                            AppointmentID = app.AppointmentID_,
                                            ReminderID = Rem.ReminderID_
                                        };

                    foreach (var reminder in nextReminders)
                    {
                        if (bStopping)  // Allow it to stop in the middle
                            break;

                        // Process each reminder

                        if ((reminder.To.Trim().Length == 0) || (reminder.TemplateID == null)) // If TO field is empty
                            continue;                                                           // or no message skip to next one

                        DateTime reminderABS = reminder.Occurs;
                        reminderABS = reminderABS.Subtract(new TimeSpan(reminder.Days, reminder.Hours, reminder.Minutes, 0));

                        if (DateTime.Now >= reminderABS) // It is time to send a reminder
                        {
                            bool bOkToSent = true;

                            // Check that the reminder has not been sent already

                            var sentReminders = from h in remindersDB.Historic
                                                where h.AppointmentID_ == reminder.AppointmentID &&
                                                    h.ReminderID_ == reminder.ReminderID
                                                select h;

                            if (sentReminders.Count() > 0) // Some reminders were already sent
                                foreach (var sentReminder in sentReminders)
                                    if (sentReminder.SentOK)
                                        bOkToSent = false;  // If any of them was sent OK then don't send again

                            if (bOkToSent)
                            {
                                string sMessage = null; // Get text for message

                                if (reminder.TemplateID == 0)
                                    sMessage = reminder.CustomMessage;
                                else
                                    sMessage = (from t in remindersDB.Templates
                                                where t.TemplateID_ == reminder.TemplateID
                                                select t.Text).Single();

                                if (sMessage.Trim().Length == 0) // If empty message text skip to next one
                                    continue;

                                // Extract fields from message

                                sMessage = sMessage.Replace("$DIA$", reminder.Occurs.ToShortDateString());
                                sMessage = sMessage.Replace("$HORA$", reminder.Occurs.ToShortTimeString());

                                // Normalize TO mobile numbers to API standard

                                string toField = "";
                                foreach (string number in reminder.To.Split(';'))
                                {
                                    if (toField.Length > 0)
                                        toField += ";";
                                    toField += number.Trim();
                                }

                                Historic historicReminder = new Historic()
                                {
                                    AppointmentID_ = reminder.AppointmentID,
                                    ReminderID_ = reminder.ReminderID,
                                    Date = DateTime.Now,
                                    SentTo = toField,
                                    Message = sMessage,
                                    SentOK = false
                                };

                                sServiceURL = LoadSetting("SERVICEURL");
                                sServiceMobile = LoadSetting("SERVICEMOBILE");
                                sServicePass = LoadSetting("SERVICEPASS");

                                if ((sServiceURL.Length == 0) || (sServicePass.Length == 0) || (sServiceMobile.Length == 0))
                                {
                                    WriteToEventLog("Parámetros de configuración de Movistar SMS inválidos", EventLogEntryType.Warning);

                                    // Call SMS HTTP gateway
                                }
                                else
                                {
                                    string sStatus = MovistarGateway.SendMessage(sServiceURL, sServiceMobile, sServicePass,
                                      reminder.To, sMessage);

                                    if (sStatus.Length > 0)
                                        WriteToLogFile(sStatus);
                                    else
                                        historicReminder.SentOK = true;
                                }

                                // Save historic information

                                remindersDB.Historic.InsertOnSubmit(historicReminder);

                                remindersDB.SubmitChanges();
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    WriteToEventLog("Error inesperado: " + e.Message, EventLogEntryType.Error);
                }

                if (!bStopping)                     // Pause 60 seconds but allow it to stop in the middle 
                    for (int i = 1; i <= 12; i++)
                    {
                        Thread.Sleep(5000);
                        if(bStopping)
                            break;
                    }
            }

            bRunning = false;
        }

        private string LoadSetting(string sKey)
        {
            var value = from setting in remindersDB.Settings
                        where setting.Key == sKey
                        select setting;

            if (value.Count() > 0)
                return (value.Single().Value);
            else
            {
                Settings newSetting = new Settings() { Key = sKey, Value = "" };

                remindersDB.Settings.InsertOnSubmit(newSetting);
                remindersDB.SubmitChanges();
            }

            return ("");
        }

        #endregion

        #region Tracing

        private void WriteToEventLog(string sMessage, EventLogEntryType type)
        {
            try
            {
                EventLog myLog = new EventLog();
                myLog.Source = "Movistar.SMS.Service";
                myLog.WriteEntry(sMessage, type);

                WriteToLogFile(sMessage);
            }
            catch { }
        }

        private void WriteToLogFile(string sMessage)
        {
            try
            {
                StreamWriter sw = new StreamWriter(sLogFilePath, true);
                sw.WriteLine(sMessage);
                sw.Flush();
                sw.Close();
            }
            catch { }
        }

        #endregion
    }
}
